#include <iostream>
#include <memory>
#include <string>
#include<functional>
#include <sys/select.h>
#include <sys/time.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
using namespace sock_ns;
using namespace ns_log;
static const int gnum = sizeof(fd_set) * 8;
static const int gdefault = -1;


using task_t = std::function<void()>;
ThreadPool<task_t> Pool;
class SelectServer
{
public:
    SelectServer(uint16_t port) : _port(port), _listensocket(std::make_unique<Tcp_socket>())
    {
        _listensocket->BuildListenSocket(_port);
        //启动线程池
        Pool.Init();
        Pool.start();
    }
    ~SelectServer()
    {
        Pool.Stop();
    }
    void InitServer()
    {
        for (int i = 0; i < gnum; i++)
        {
            fd_array[i] = gdefault;
        }
        fd_array[0] = _listensocket->SockFd();
    }
    void Accepter()
    {
        // 监听套接字处理
        // 有读事件就绪！
        InetAddr addr;
        int sockfd = _listensocket->AcceptSocket(&addr);
        if (sockfd > 0)
        {
            bool flag = false; // 标记位，用于判断fd_array是否已满
            LOG(INFO, "Get a new link,mssage = %s\n", addr.GetAddrStr().c_str());
            // 将新的文件fd交给select统一监管！
            for (int i = 1; i < gnum; i++)
            {
                if (fd_array[i] == gdefault)
                {
                    flag = true;
                    fd_array[i] = sockfd;
                    break;
                }
            }
            if (!flag)
            {
                LOG(WARNING, "Server is full!\n");
                ::close(sockfd);
            }
        }
    }
    void HandleIo()
    {
        // 普通套接字处理
        char buffer[1024];
        ssize_t n = ::recv(fd_array[pos], &buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "Client say# " << buffer << std::endl;
            std::string send_str = "[server info]@ ";
            send_str += buffer;
            ::send(fd_array[pos], send_str.c_str(), send_str.size(), 0);
        }
        else if (n == 0)
        {
            ::close(fd_array[pos]);
            fd_array[pos] = gdefault;
            LOG(INFO, "client quit...\n");
        }
        else
        {
            ::close(fd_array[pos]);
            fd_array[pos] = gdefault;
            LOG(ERROR, "recv error!\n");
        }
    }
    void HandleHappen(fd_set &rd_set)
    {
        for (int i = 0; i < gnum; i++)
        {
            if (fd_array[i] == gdefault)
                continue;
            // 是否在fd_set中?
            if (FD_ISSET(fd_array[i], &rd_set))
            {
                // 有可能是监听套接字Or普通套接字;
                if (_listensocket->SockFd() == fd_array[i])
                {
                    task_t task = std::bind(&SelectServer::Accepter,this);
                    //处理新的连接;
                    Pool.Push(task);
                }
                else     
                {
                    pos = i;
                    task_t task = std::bind(&SelectServer::HandleIo,this);
                    //处理新的连接;
                    Pool.Push(task);
                    //处理正常IO;
                }
            }
        }
    }
    void PrintDebug()
    {
        std::cout << "list: ";
        for (int i = 0; i < gnum; i++)
        {
            if (fd_array[i] == gdefault)
                continue;
            std::cout << fd_array[i] << " ";
        }
        std::cout<<std::endl;
    }
    void Loop()
    {
        while (true)
        {
            int maxfd = -1; // 待更新的fd的最大值
            fd_set rd_set;
            struct timeval timeout = {1, 0};
            FD_ZERO(&rd_set);
            // 合法的文件fd添加进fd_set中！
            for (int i = 0; i < gnum; i++)
            {
                if (fd_array[i] == gdefault)
                    continue;
                FD_SET(fd_array[i], &rd_set);
                if (maxfd < fd_array[i])
                {
                    maxfd = fd_array[i];
                }
            }
            int n = ::select(maxfd + 1, &rd_set, nullptr, nullptr, &timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "timeout...\n");
                break;
            case -1:
                LOG(ERROR, "select error!\n");
                break;
            default:
                LOG(INFO, "有事件就绪!\n");
                HandleHappen(rd_set);
                PrintDebug();
                break;
            }
        }
    }

private:
    uint16_t _port;                        // 端口号
    std::unique_ptr<Socket> _listensocket; // 监听套接字
    int fd_array[gnum];                    // 辅助数组,记录所有合法的文件fd
    int pos = -1;
};